class GameMap {
    constructor(image_map, def_image_key, pixel_width, pixel_height, width, height, fun_push_image) {
        this.image_map = image_map;
		this.def_image_key = def_image_key;
        this.pixel_width  = pixel_width;
        this.pixel_height = pixel_height;
        this.width  = width;
        this.height = height;
        this.fun_push_image = fun_push_image;
		this.block_values = [];
    }

    init() {
		if (!(this.def_image_key in this.image_map)) {
			return false;
		}
		this.reset();
		return true;
    }

	reset() {
		this.block_values = [];
		for (var i = 0; i < this.pixel_width; i++) {
			var block_values = []
			for (var j = 0; j < this.pixel_height; j++) {
				block_values.push(this.def_image_key);
			}
			this.block_values.push(block_values);
		}
	}

    draw() {
        for (var i = 0; i < this.pixel_width; i++) {
            for (var j = 0; j < this.pixel_height; j++) {
				var pt = {
					x: this.width * i, 
					y: this.height * j
				};
				var options = {
					'width':  this.width,
					'height': this.height
				};
				this.fun_push_image(
					this.image_map[this.block_values[i][j]],
					pt, 
					options
				);
            }
        }
    }

    get_point(x, y) {
        return {
            x1: x * this.width, 
            y1: y * this.height, 
            x2: (x + 1) * this.width,
            y2: (y + 1) * this.height
        };
    }

    set_block_image(x, y, image_key) {
        if (x >= this.pixel_width) {
			return {ok: false, errormsg: 'x exceed'};
        }
        if (y >= this.pixel_height) {
			return {ok: false, errormsg: 'y exceed'};
        }
		if (!(image_key in this.image_map)) {
			return {ok: false, errormsg: 'image_key not exist'};
		}
        this.block_values[x][y] = image_key;
        return {ok: true, errormsg: ''};
    }

    get_block_value(x, y) {
        if (x >= this.pixel_width) {
			return {ok: false, errormsg: 'x exceed', value: null};
        }
        if (y >= this.pixel_height) {
			return {ok: false, errormsg: 'y exceed', value: null};
        }
        return {ok: true, errormsg: '', value: this.block_values[x][y]};
    }

    get_pixel_width() {
        return this.pixel_width;
    }

    get_pixel_height() {
        return this.pixel_height;
    }

    get_block_width() {
        return this.width;
    }

    get_block_height() {
        return this.height;
    }
}

class GameUtils {

	is_in_set_pos(pos, set_pos) {
		for (var item of set_pos) {
			if (item.x == pos.x && item.y == pos.y) {
				return true;
			}
		}
		return false;
	}

	get_cycle_path(pos, radius, width, height) {
		var set = new Set();
		this.set_cycle_path_recursion(set, pos.x, pos.y, radius, width, height);
		return set;
	}

	set_cycle_path_recursion(set, x, y, radius, width, height) {
		if (radius == 0) {
			return ;
		}
    	// up
    	if (y > 0) {
			set.add({x: x, y: y - 1});
    	    this.set_cycle_path_recursion(set, x, y - 1, radius - 1, width, height);
    	}
    	// down
    	if (y < height - 1) {
			set.add({x: x, y: y + 1});
    	    this.set_cycle_path_recursion(set, x, y + 1, radius - 1, width, height);
    	}
    	// left
    	if (x > 0) {
			set.add({x: x - 1, y: y});
    	    this.set_cycle_path_recursion(set, x - 1 , y, radius - 1, width, height);
    	}
    	// right
    	if (x < width - 1) {
			set.add({x: x + 1, y: y});
    	    this.set_cycle_path_recursion(set, x + 1, y, radius - 1, width, height);
    	}
	}
}

class GameImage {
    constructor(map, image, fun_push_image, options = {}) {
		this.map   = map;
        this.image = image;
        this.options = options;
		this.fun_push_image = fun_push_image;
    }

    draw(pos) {
		var pt = {
			x: pos.x * this.map.get_block_width(),
			y: pos.y * this.map.get_block_height()
		};
		this.fun_push_image(this.image, pt, this.options);
    }
}
/*
class GameImageCycle {
    constructor(map, image, len, fun_push_image) {
        this.map   = map;
        this.image = image;
        this.len   = len;
		this.fun_push_image = fun_push_image;
    }

	draw(pt) {

		var draw_set = new Set();
		this.set_path(draw_set, pt.x, pt.y, this.len);

		var options = {
			'width':  this.map.get_block_width(),
			'height': this.map.get_block_height()
		};

		for (var item of draw_set) {
			var arr = item.split(',');	
			var draw_pt = {
				x: pt.x + Number(arr[0]) * this.map.get_block_width(),
				y: pt.y + Number(arr[1]) * this.map.get_block_height()
			};
			console.info('++++++++++ GameImageCycle, pt:', draw_pt);
			this.fun_push_image(this.image, draw_pt, options);
		}
	}

    set_path(set, x, y, l) {
        if (l == 0) {
            return ;
        }
        // up
        if (y > 0) {
            set.add(this.pos_to_string(y - 1, x));
            this.set_path(set, x, y - 1, l - 1);
        }
        // down
        if (y < this.map.get_pixel_height() - 1) {
            set.add(this.pos_to_string(y + 1, x));
            this.set_path(set, x, y + 1, l - 1);
        }
        // left
        if (x > 0) {
            set.add(this.pos_to_string(y, x - 1));
            this.set_path(set, x - 1 , y, l - 1);
        }
        // right
        if (x < this.map.get_pixel_width() - 1) {
            set.add(this.pos_to_string(y, x + 1));
            this.set_path(set, x + 1, y, l - 1);
        }
    }

    pos_to_string(x, y) {
        return y.toString() + ',' + x.toString();
    }
}
*/
class GameImageCycle {
    constructor(map, image, len, fun_push_image) {
        this.map   = map;
        this.image = image;
        this.len   = len;
		this.fun_push_image = fun_push_image;
    }

	draw(pos) {

		var util = new GameUtils;
		var pos_set = util.get_cycle_path(
			pos, 
			this.len, 
			this.map.get_pixel_width(), 
			this.map.get_pixel_height()
		);

		var options = {
			'width':  this.map.get_block_width(),
			'height': this.map.get_block_height()
		};

		for (var pos of pos_set) {
			var draw_pt = {
				x: Number(pos.x) * this.map.get_block_width(),
				y: Number(pos.y) * this.map.get_block_height()
			};
			this.fun_push_image(this.image, draw_pt, options);
		}
	}
}
/*
class GameSelectRect {
    constructor(map, pt, options = {}) {
        this.map = map;
        this.pt = pt;
        this.lineWidth = 2;
        this.strokeStyle = 'blue';
        if ('line_width' in options) {
            this.lineWidth = options['line_width'];
        }
        if ('stroke_style' in options) {
            this.strokeStyle = options['stroke_style'];
        }
    }

    up() {
        if (this.pt.y > 0) {
            this.pt.y--;
        }
    }

    down() {
        if (this.pt.y < this.map.get_pixel_height() - 1) {
            this.pt.y++;
        }
    }

    left() {
        if (this.pt.x > 0) {
            this.pt.x--;
        }
    }

    right() {
        if (this.pt.x < this.map.get_pixel_width() - 1) {
            this.pt.x++;
        }
    }

    draw(ctx) {
        var x1 = this.pt.x * this.map.get_block_width();
        var y1 = this.pt.y * this.map.get_block_height();
        var x2 = x1 + this.map.get_block_width();
        var y2 = y1 + this.map.get_block_height();

        var draw_lines = [
            {x1: x1, y1: y1, x2: x2, y2: y1},
            {x1: x2, y1: y1, x2: x2, y2: y2},
            {x1: x1, y1: y2, x2: x2, y2: y2},
            {x1, x1, y1: y1, x2: x1, y2: y2}
        ];
        for (var i = 0; i < draw_lines.length; i++) {
			var line = draw_lines[i];
            ctx.beginPath();
            ctx.moveTo(line.x1, line.y1);
            ctx.lineTo(line.x2, line.y2);
            ctx.lineWidth   = this.lineWidth;
            ctx.strokeStyle = this.strokeStyle;
            ctx.stroke();
        }
    }

    get_pos() {
        return {
			x: this.pt.x, 
			y: this.pt.y
		};
    }
}
*/

class GameSelectRect {
    constructor(map, pos, options = {}) {
        this.map = map;
        this.pos = pos;
        this.lineWidth = 2;
        this.strokeStyle = 'blue';
        if ('line_width' in options) {
            this.lineWidth = options['line_width'];
        }
        if ('stroke_style' in options) {
            this.strokeStyle = options['stroke_style'];
        }
    }

    up() {
        if (this.pos.y > 0) {
            this.pos.y--;
        }
    }

    down() {
        if (this.pos.y < this.map.get_pixel_height() - 1) {
            this.pos.y++;
        }
    }

    left() {
        if (this.pos.x > 0) {
            this.pos.x--;
        }
    }

    right() {
        if (this.pos.x < this.map.get_pixel_width() - 1) {
            this.pos.x++;
        }
    }

    draw(ctx) {
        var x1 = this.pos.x * this.map.get_block_width();
        var y1 = this.pos.y * this.map.get_block_height();
        var x2 = x1 + this.map.get_block_width();
        var y2 = y1 + this.map.get_block_height();

        var draw_lines = [
            {x1: x1, y1: y1, x2: x2, y2: y1},
            {x1: x2, y1: y1, x2: x2, y2: y2},
            {x1: x1, y1: y2, x2: x2, y2: y2},
            {x1, x1, y1: y1, x2: x1, y2: y2}
        ];
        for (var i = 0; i < draw_lines.length; i++) {
			var line = draw_lines[i];
            ctx.beginPath();
            ctx.moveTo(line.x1, line.y1);
            ctx.lineTo(line.x2, line.y2);
            ctx.lineWidth   = this.lineWidth;
            ctx.strokeStyle = this.strokeStyle;
            ctx.stroke();
        }
    }

	set_pos(pos) {
		this.pos = pos;
	}

    get_pos() {
		return this.pos;
    }
}

class GameAnimation {
    constructor(map, images, pt, fun_push_image, options = {}) {
		this.map = map;
        this.images = images;
        this.pt = pt;
        this.fun_push_image = fun_push_image;
        this.options = options;
        this.index = 0;
        this.loop  = true;

        if ('loop' in options) {
            this.loop = options['loop'];
        }

		this.options['width']  = map.get_block_width();
		this.options['height'] = map.get_block_height();
    }

    update() {
        if ((this.index + 1) % this.images.length == 0 && this.loop == false) {
            return ;
        }
		this.fun_push_image(this.image, this.pt, this.options);
    }
}

class MenuItem {
    constructor(id, text) {
        this.id   = id;
        this.text = text;
    }
}

class Menu {
    constructor(theme_id, menu_items) {
        this.theme_id   = theme_id;
        this.menu_items = menu_items;
        this.sel_index  = 0;
    }

    set_item_text(id, text) {
		for (var i = 0; i < this.menu_items.length; i++) {
			if (this.menu_items[i].id == id) {
				this.menu_items[i].text = text;
			}
		}
    }
}

class GameMenu {
    constructor(map, menu, options = {}) {
		this.map    = map;
        this.menu   = menu;
        this.width  = 100;
        this.height = 40;
        this.font   = '14px Arial';
        this.sel_index = 0;
        this.sel_color = 'red';
        this.def_color = 'rgba(191, 191, 191, 0.5)';
		this.visible = false;

        if ('width' in options) {
            this.width = options['width'];
        }
        if ('height' in options) {
            this.height = options['height'];
        }
        if ('font' in options) {
            this.font = options['font'];
        }
    }

    reset() {
        this.menu = null;
    }

    set_menu(menu) {
        this.menu = menu;
        this.sel_index = 0;
    }

	set_visible(visible) {
		this.visible = visible;
	}

    up() {
        if (this.sel_index > 0) {
            this.sel_index--;
        }
    }

    down() {
        if (this.sel_index != this.menu.menu_items.length - 1) {
            this.sel_index++;
        }
    }

    get_theme_id() {
        return this.menu.theme_id;
    }

    get_item_count() {
        return this.menu.menu_items.length;
    }

    get_sel_menu_item() {
        return this.menu.menu_items[this.sel_index];
    }
/*
    draw(ctx, pt) {
        if (this.visible == false || this.menu == null) {
            return ;
        }
        for (var i = 0; i < this.menu.menu_items.length; i++) {
            ctx.fillStyle = this.def_color;
            if (this.sel_index == i) {
                ctx.fillStyle = this.sel_color;
            }
            var x = pt.x;
            var y = pt.y + i * this.height;
            ctx.fillRect(x, y, this.width, this.height);
            ctx.fillStyle = 'black';
            ctx.fillText(this.menu.menu_items[i].text, x + 5, y + 20);
        }
    }
*/

	draw(ctx, pos) {
        if (this.visible == false || this.menu == null) {
            return ;
        }
		var pt = {
			x: pos.x * this.map.get_block_width(),
			y: pos.y * this.map.get_block_height()
		};
		for (var i = 0; i < this.menu.menu_items.length; i++) {
            ctx.fillStyle = this.def_color;
            if (this.sel_index == i) {
                ctx.fillStyle = this.sel_color;
            }
			var x = pt.x;
			var y = pt.y + i * this.height;
            ctx.fillRect(x, y, this.width, this.height);
            ctx.fillStyle = 'black';
            ctx.fillText(this.menu.menu_items[i].text, x + 5, y + 20);
		}
	}
}

export { GameMap }
export { GameImage } 
export { GameImageCycle }
export { GameSelectRect }
export { GameAnimation }
export { MenuItem }
export { Menu }
export { GameMenu } 
export { GameUtils } 

